/*
 * Galaxium Messenger
 * Copyright (C) 2003-2007 Philippe Durand <draekz@gmail.com>
 * Copyright (C) 2007 Paul Burton <paulburton89@gmail.com>
 * Copyright (C) 2007 Ben Motmans <ben.motmans@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Globalization;

using Anculus.Core;

using Galaxium.Core;
using Galaxium.Protocol;

namespace Galaxium.Gui
{
	public static class MessageUtility
	{
		private static Regex _regexHyperlinks;
		private static Regex _regexEmails = new Regex (@"([\w\d\._-]+@[\w\d\._-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
		
		static Dictionary<IEntity, List<string>> _cachedSearchLists = new Dictionary<IEntity, List<string>> ();
		static List<string> _uriSearchStrings = new List<string> ();
		
		static Dictionary<IProtocol, IMessageSplitter> _splitters = new Dictionary<IProtocol, IMessageSplitter> ();
		
		public static string[] UriStarts = { "http://", "https://", "file://", "ftp://", "mms://", "www.", "ftp." };
		
		public static char[] WhitespaceChars = { ' ', '\t', '\r', '\n' };
		
		static MessageUtility ()
		{
			_uriSearchStrings.AddRange (UriStarts);
			
			_regexHyperlinks = new Regex (@"((" + string.Join ("|", UriStarts) + @")[\w\d\.\/\+&;=_:?-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
		}
		
		//TODO: the ParseHyperlinks function should be re-used inside this function if possible
		public static List<ITextChunk> SplitMessage (string message, TextStyle style, IProtocol protocol, IEntity entity, List<IEmoticon> customEmoticons)
		{
			List<ITextChunk> messageChunks;
			
			lock (_splitters)
			{
				CreateSplitter (protocol);
				
				if (_splitters.ContainsKey (protocol))
					messageChunks = _splitters[protocol].SplitMessage (message, style, entity, customEmoticons);
				else
				{
					messageChunks = new List<ITextChunk> ();
					messageChunks.Add (new TextChunk (style, TextChunkType.Text, message));
				}
			}
			
			IContact dest = entity as IContact;
			IAccount account = (dest != null) ? dest.Session.Account : entity as IAccount;
			bool outgoing = (entity != null) ? entity.Local : false;
			
			OrderedDictionary<string, IEmoticon> emots = EmoticonUtility.GetEmoticonDict (account, dest, customEmoticons, outgoing);
			
			List<string> searchList = new List<string> ();
			searchList.Add ("\n");
			searchList.AddRange (_uriSearchStrings);
			searchList.AddRange (emots.Keys);
			
			List<ITextChunk> chunks = new List<ITextChunk>();
			
			foreach (ITextChunk msgChunk in messageChunks)
			{
				string chunkTxt = msgChunk.Text.Replace ("\r\n", "\n").Replace ("\r", "\n");
				SearchResult[] results = SetSearch.SearchAll (chunkTxt, searchList.ToArray ());
				
				int pos = 0;
				
				foreach (SearchResult result in results)
				{
					if (pos < result.Index)
					{
						// Add any text which came before this match
						
						chunks.Add (new TextChunk (msgChunk.Style, TextChunkType.Text, chunkTxt.Substring (pos, result.Index - pos)));
						pos = result.Index;
					}
					else if (pos > result.Index)
					{
						// This could happen if we have a URL containing an emoticon equivalent
						continue;
					}
					
					if (result.Match == "\n")
					{
						// PangoUtility expects new lines to be in separate chunks
						
						chunks.Add (new TextChunk (msgChunk.Style, TextChunkType.Text, "\n"));
						pos++;
					}
					else if (_uriSearchStrings.Contains (result.Match))
					{
						// This is a hyperlink
						
						int end = chunkTxt.IndexOfAny (WhitespaceChars, result.Index);
						string url = (end < 0) ? chunkTxt.Substring (result.Index) : chunkTxt.Substring (result.Index, end - result.Index);
						
						chunks.Add (new TextChunk (msgChunk.Style, TextChunkType.URL, url));
						
						pos += url.Length;
					}
					else
					{
						// This is an emoticon
						
						chunks.Add (new EmoticonTextChunk (msgChunk.Style, result.Match, emots[result.Match]));
						pos += result.Length;
					}
				}
				
				// Any remaining text after the last match
				if (pos < chunkTxt.Length)
					chunks.Add (new TextChunk (msgChunk.Style, TextChunkType.Text, chunkTxt.Substring (pos)));
			}
			
			return chunks;
		}
		
		public static IEnumerable<ITextChunk> ParseHyperlinks (string text, ITextStyle style, TextChunkType type)
		{
			MatchCollection matches = _regexHyperlinks.Matches (text);
			if (matches.Count == 0) {
				yield return new TextChunk (style, type, text);
			} else {
				int prev = 0;
				ITextChunk subChunk = null;
				foreach (Match match in matches) {
					if (prev < match.Index) {
						string chunkText = text.Substring (prev, match.Index - prev);
						subChunk = new TextChunk (style, type, chunkText);
						yield return subChunk;
					}
					
					subChunk = new TextChunk (style, TextChunkType.URL, match.Value.ToString ());
					yield return subChunk;
					
					prev = match.Index + match.Length;
				}
				
				if (prev < (text.Length - 1)) {
					string chunkText = text.Substring (prev);
					subChunk = new TextChunk (style, type, chunkText);
					yield return subChunk;
				}
			}
		}
		
		// Always call from within lock (_splitters)
		static void CreateSplitter (IProtocol protocol)
		{
			if (!_splitters.ContainsKey (protocol))
			{
				IMessageSplitter splitter = null;
				IProtocolFactory factory = ProtocolUtility.GetProtocolFactory (protocol);
				
				// Example protocol for the message display prefs doesn't have a factory
				if (factory != null)
					splitter = factory.CreateSplitter ();
				
				if (splitter != null)
					_splitters.Add (protocol, splitter);
			}
		}
		
		public static string StripMarkup (string message, IProtocol protocol)
		{
			List<ITextChunk> chunks;
			
			lock (_splitters)
			{
				CreateSplitter (protocol);
				
				if (!_splitters.ContainsKey (protocol))
					return message;
				
				chunks = _splitters[protocol].SplitMessage (message, new TextStyle (), null, null);
			}
			
			string output = string.Empty;
			
			foreach (ITextChunk chunk in chunks)
				output += chunk.Text;
			
			return output;
		}
	}
}
